Deterministic and quasi-deterministic solutions ================================================== Most of RISE solves models by *perturbation* around the steady state: the solution is a (possibly high-order) policy function and shocks are treated as random. **Deterministic** solutions take the opposite stance — the entire path of the exogenous variables is treated as *known* (perfect foresight), and RISE solves the model's equations directly along that path. There is no approximation of the policy function: the nonlinear equations are imposed exactly at every date, subject to an initial condition and a terminal condition. This is the right tool when the experiment is fundamentally about a *known* future: - a **permanent change** in an exogenous variable or a parameter (e.g. a tax reform), where the economy transitions from one steady state to another; - **anticipated** (pre-announced) shocks — agents know today about a change that materialises later; - large disturbances for which a local approximation around the steady state is inaccurate; - **occasionally-binding constraints** solved by extended path (see :doc:`occasionally-binding constraints <../OccasionallyBindingConstraints/OCB>`). It is *not* a substitute for stochastic perturbation when you care about risk, ergodic moments, or the effect of uncertainty itself: perfect foresight sets all future shocks to their expected (known) values. The perfect-foresight problem ------------------------------- Write the model compactly as .. math:: f\left(y_{t-1},\, y_{t},\, y_{t+1},\, x_{t}\right) = 0, \qquad t = 1,\dots,T, where :math:`y_t` are the endogenous variables and :math:`x_t` the (known) exogenous variables. A perfect-foresight problem is a two-point boundary-value problem: - an **initial condition** pins the predetermined variables at :math:`t=0` (typically a steady state, or the realised state at the start of the simulation); - a **terminal condition** pins :math:`y_{T+1}` (typically the steady state the economy converges back to); - the solver finds the interior path :math:`y_1,\dots,y_T` that satisfies every equation at every date. Stacking the :math:`T` blocks gives one large nonlinear system in :math:`T \times` ``endo_nbr`` unknowns. RISE solves it with a stacked Newton method (see `perfect_foresight`). A first example ~~~~~~~~~~~~~~~~~ Consider a small growth model with capital ``k``, consumption ``c`` and an exogenous ``x``. Suppose the economy starts away from its steady state — say capital is at half its long-run value — and we want the transition back. After parsing the model and solving its steady state, build a **simulation plan** with `simplan`, set the initial condition, and call `perfect_foresight` :: m = rise('my_growth_model'); m = set(m, 'parameters', my_params()); m = sstate(m, 'sstate_file', @my_sstate); ss = get(m, 'sstate'); horizon = 200; plan = simplan(m, [0, horizon], 1); % dates 0..horizon, 1 page % initial condition at period 0: capital starts at half the steady state plan = append(plan, 'Capital', 0, ss.Capital/2); [sims, fval, retcode] = perfect_foresight(m, 'simul_historical_data', plan); ``sims`` is a structure of time series (one per variable); ``fval`` is the final (max-abs) residual of the stacked system, and ``retcode`` is ``0`` on success (use ``decipher(retcode)`` otherwise). The terminal condition defaults to the model's steady state, so the path naturally settles back. Setting the conditions ----------------------- The simulation plan carries all the boundary information. The most direct way to set it is `append`, which pins a variable to a value at one or more dates:: plan = append(plan, 'Capital', 0, ss.Capital/2); % initial condition plan = append(plan, 'x', 2, 0.9); % a known shock at t=2 plan = append(plan, S, 0); % a whole struct at date 0 Anything left free (``NaN``) at the start and end falls back to the steady state, and free interior entries are what the solver computes. Initial and terminal steady states ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A permanent-change experiment has a *different* initial and terminal steady state. The clean way to express it is to solve the steady state of the **terminal** environment and run `perfect_foresight` on that model — its steady state then supplies both the terminal condition and the long-run value of the exogenous variables — while pinning the period-0 endogenous variables to the **initial** steady state. There is no need to solve a separate "terminal steady state" by hand: the terminal block of the model is solved as part of the problem. .. note:: RISE's `simplan` also offers ``initval`` and ``endval`` methods that mirror Dynare's blocks (each takes a *steady* flag: ``true`` solves a steady state from the supplied guess, ``false`` uses the values as given). They are convenient when porting a Dynare file, but for new work passing the conditions directly with `append` is usually clearer. Choosing the horizon --------------------- The horizon :math:`T` is not innocuous, and there is a genuine trade-off: - **Too short** and the terminal steady state is imposed before the transition has actually died out. The boundary condition is then inconsistent with the dynamics, and the solver either fails to drive the residuals to zero or returns a distorted path. The horizon must outlast the slowest stable transient — a rule of thumb is several times the half-life of the dominant stable eigenvalue of the first-order solution. - **Too long** and the stacked system grows: the residual/Jacobian evaluation cost is linear in :math:`T`, but the cost of the linear solve grows faster, and memory with it. When in doubt, err long and lean on the solver options below to keep the cost down (reusing the factorization makes a longer horizon much cheaper). Solvers and performance ------------------------ The stacked system is solved by the algorithm named in **simul_stack_solve_algo**: ``'sparse'`` (default) A damped Newton method with an Armijo line search and a robust sparse linear solve (``rise_newton``). Appropriate for the *square* stacked system and the default choice. ``'lsqnonlin'`` / ``'fsolve'`` MATLAB's nonlinear least-squares / root-finding solvers. ``lsqnonlin`` honours box constraints on the variables; ``fsolve`` does not. a function handle, a ``{algo, opts}`` cell, or a nested fallback chain ``{ {'sparse',opts1}, {'lsqnonlin',opts2} }`` tries each in turn until one succeeds. Two block decompositions are exploited automatically. First, RISE block-triangularises the system, so recursive (purely static / backward / forward) blocks are solved cheaply and only the genuinely simultaneous block carries the full stacked solve. Second, within a block the per-period ``[A_{-},\,A_0,\,A_{+}]`` structure is assembled directly as a sparse Jacobian. Reusing the Jacobian factorization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For large horizons the dominant cost is building and **factorizing** the stacked Jacobian. A full Newton step refactorizes every iteration, but near the solution the Jacobian barely changes, so the factorization can be reused. The ``'sparse'`` solver supports this through the **JacobianUpdate** option (passed inside the solver options): - ``1`` (default) — full Newton: rebuild and refactorize every iteration. - ``inf`` — modified Newton: factorize **once** and reuse the factors, refreshing adaptively only when the line search has to backtrack (and retrying with a fresh factorization if a reused one stalls). - any ``K > 1`` — refactorize every ``K`` iterations. :: [~, opts] = generic_tools.reset_optim_options('sparse', 'solve'); opts.JacobianUpdate = inf; % factor once, reuse [sims, fval, rc] = perfect_foresight(m, 'simul_historical_data', plan, ... 'simul_stack_solve_algo', {'sparse', opts}); Reuse trades quadratic for linear convergence but makes each iteration far cheaper; on transition problems it leaves the solution unchanged while substantially reducing the time spent in the linear solve. Reusing the steady state ~~~~~~~~~~~~~~~~~~~~~~~~~~~ `perfect_foresight` solves the model's steady state before simulating. When the model handed in is already solved and its parameters have not changed — for instance when running several simulations in a loop, or an extended path — the re-solve is wasteful. Set **simul_reuse_sstate** to ``true`` to skip it and use the model's stored steady state:: [sims, fval, rc] = perfect_foresight(m, 'simul_historical_data', plan, ... 'simul_reuse_sstate', true); The default is ``false`` so the normal solve-on-demand behaviour is unchanged. Other options ~~~~~~~~~~~~~~~ **simul_enforce_hybrid** Force every block to be solved as a hybrid (both backward- and forward-looking) block, bypassing the static/forward/backward classification. **simul_pf_bounds** A structure of box constraints on the endogenous variables held during the simulation. Convergence and tolerances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Convergence of the ``'sparse'`` solver is judged on the **max-abs** (infinity norm) of the residual, not the 2-norm. This matters at scale: the 2-norm of a stacked residual grows like :math:`\sqrt{T\times\text{endo\_nbr}}`, so a fixed 2-norm tolerance becomes unreachable for long horizons even when every individual equation residual is tiny. The max-abs criterion is per-equation and therefore size-independent. The tolerance you can *meaningfully* demand is bounded by the accuracy of the Jacobian. With an exact (symbolic) Jacobian the residual can be driven to machine precision; the achievable floor is otherwise set by the differentiation method. There is no need to scale the tolerance with the number of equations — the max-abs criterion already does that. Quasi-deterministic solutions: the extended path ------------------------------------------------- The pure perfect-foresight solve assumes the agents' information about the future never changes. The **extended path** relaxes this: the economy is re-solved period by period, and at each date new ("surprise") information can arrive. This is the natural setting for stochastic simulation of nonlinear models and for occasionally-binding constraints, where the binding/slack pattern is discovered as the simulation unfolds. In RISE this is triggered by supplying a multi-page simulation plan (``npages > 1``): page 1 is the realised/base path and pages ``2:npages`` carry the conditioning information revealed at each date. At every pass the solver window shrinks by one period; when no surprise arrives at a date and a previous solution exists, RISE shifts that solution by one period rather than re-solving (a cheap "no-surprise" shortcut). The output then additionally carries an ``expectations`` field with the step-ahead expectations formed at each date. Conditional forecasting — pinning future endogenous variables and letting RISE back out the shocks that achieve them — is a closely related use of the same machinery; see the forecasting material and `simplan`. Regime switching ---------------- Unlike most perfect-foresight tools, RISE carries a **regime sequence** through the deterministic solve: each period can be assigned a Markov-chain regime, and the per-period equations and steady-state values are evaluated for that regime. This makes it possible to run deterministic simulations under known or anticipated regime changes — a capability that combines RISE's Markov-switching machinery with the perfect-foresight algorithm. See also -------- - :doc:`the steady state and balanced-growth path <../Local Approximation/Solving the Balanced-growth path and steady state>` — the boundary conditions of every deterministic problem. - :doc:`occasionally-binding constraints <../OccasionallyBindingConstraints/OCB>` — extended path and piecewise-linear approaches to OBC. - `perfect_foresight`, `simplan`, `simulate` — the API.